using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.IO;

namespace GAscheduler
{
   
    /// <summary>
    /// Contains the main Genetic Algorithm code - Manages our sets of schedules
    /// Managing involves generating random ones and breeding good ones together for new generations
    /// 
    /// Avoiding extra lines of singleton code by using static
    /// Downside is a stronger reliance on UML Class diagram to show relationships among classes
    /// </summary>
    static class Schedule_Manager
    {
        public static int population = 50;//Quantity of schedules in each generation
        public static int bestPopulation = 5;//Quantity of good schedules to preserve through generations
        public static bool continueBreeding = false;//True to constantly breed, false to pause
        static Thread breedThread = new Thread(new ThreadStart(run));

        //The current pool of generated schedules
        static Schedule[] schedules = new Schedule[population];

        /// <summary>
        /// Generate random schedules to start
        /// </summary>
        public static void init()
        {
            for (int i = 0; i < population; i++)
                schedules[i] = new Schedule();//Going to allow invalid schedules to be bred away
            breedThread.Start();
        }

        public static void destroy()
        {
            breedThread.Abort();
        }
        
        static void run()
        {
            while(true)
              if(continueBreeding)
                breed();
        }

        /// <summary>
        /// Sort schedules by fitness, preserve the best performers, mutate and breed the rest
        /// Overwrite the old generation with new ones, keeping old ones that are still best
        /// </summary>
        public static void breed()
        {
            //Next generation will temporarily hold all possible children, then be pruned
            List<Schedule> nextGen = new List<Schedule>();

            //Sort schedules by fitness
            Array.Sort(schedules);

            //Preserve the top performing schedules between generations, so we never backtrack
            for(int i = 0; i < bestPopulation; i++)
              nextGen.Add(schedules[i]);

            //Breed every schedule with every other schedule
            for(int i = 0; i < population; i++)
              for(int j = i+1; j < population; j++)
                nextGen.Add(new Schedule(schedules[i], schedules[j]));
            
            //Include the previous schedules within the mix, mutated
            for(int i = 0; i < population; i++)
              nextGen.Add(new Schedule(schedules[i]));

            //Sort the new generation by fitness
            nextGen.Sort();

            //Overwrite the old generation with the best of the new generation
            for(int i = 0; i < population; i++)
              schedules[i] = nextGen[i];
        }

        public static void save(string filename)
        {
            string[] fileContents = new string[bestPopulation];

            for (int i = 0; i < bestPopulation; i++)
            {
                fileContents[i] = "Subject, Course Number, Teacher, Capacity, Room, Days, Time, Duration\n";
                foreach (Gene g in schedules[i])
                {
                    Course outCourse = CourseManager.get(g.course);
                    Room outRoom = RoomManager.get(g.room);
                    Period outPeriod = PeriodManager.get(g.period);
                    string sDays = "";
                    for (int j = 0; j < outPeriod.Days.Length; j++)
                    {
                        sDays += outPeriod.Days[j] + " ";
                    }
                    fileContents[i] += outCourse.Subject + ", "
                                    + outCourse.CourseNum + ", "
                                    + outCourse.Teacher + ", "
                                    + outCourse.Capacity + ", "
                                    + outRoom.Building + " " + outRoom.RoomNumber + ", "
                                    + sDays + ", "
                                    + outPeriod.StartTime + ", "
                                    + outPeriod.Duration + "\n";//these 2 need to be changed
                }                                                        //to reference inside respective classes
            }                                                    
            StreamWriter swOutFile = new StreamWriter(filename);
            for (int i = 0; i < bestPopulation; i++){
                swOutFile.Write("Schedule Option " + i + "/n" + fileContents[i] + "\n \n");
            }
            swOutFile.Close();
            swOutFile.Dispose();
        }

        /// <summary>
        /// Return a CSV list of all the current fitnesses
        /// </summary>
        /// <returns></returns>
        public static new string ToString()
        {
            string summary = "";
            foreach (Schedule s in schedules)
                summary += s.ToString()+",";
            return summary;
        }
    }
}
